Repository navigation
Keep page feedback from readers who decline cookies - #480
eugenia-scandit wants to merge 11 commits into
Conversation
|
| ): Promise<boolean> { | ||
| if (!endpoint) return false; | ||
| try { | ||
| await fetch(endpoint, { |
There was a problem hiding this comment.
This needs a privacy/consent review: the disclosure no longer matches where the data goes. For a reader who declined cookies, this sends the comment, page URL and title to script.google.com, and the request carries their IP and user agent. That's a different processor and different storage (a spreadsheet) than the consent banner covers. The widget's note (index.tsx:376) still says the comment is "sent to our analytics tool with this page's address", which is no longer accurate on this path.
There was a problem hiding this comment.
Partly addressed, still open: this needs a privacy/consent sign-off.
The disclosure now matches the destination (7c8da0b, 5507dce): on the fallback path, the note says the comment, the vote and the page's address and title go to a Google Apps Script form that adds them to a spreadsheet, and that the request carries the browser's IP address and user agent. On the analytics path, the note is unchanged.
What code can't settle: a reader who declines the cookie banner is exactly who the fallback sends to. That's the point of this PR, so someone responsible for privacy and consent needs to confirm it's acceptable before this ships.
| // Set from the environment so a redeploy of the Web App (which changes the | ||
| // URL) needs no code change, and so a fork or a preview build does not | ||
| // silently post into the production sheet. | ||
| feedbackEndpoint: process.env.DOCS_FEEDBACK_ENDPOINT ?? "", |
There was a problem hiding this comment.
No build sets DOCS_FEEDBACK_ENDPOINT or DOCS_FEEDBACK_TOKEN. build-docs.yml (line 61) and docs-preview.yml run yarn build with no env block, so in production this is '', sendDirectFeedback returns false, and the fallback ships turned off. Please add the variables (as secrets or vars) to the deploy workflow, or this PR has no effect.
There was a problem hiding this comment.
Still open: the variables don't reach production yet.
I added DOCS_FEEDBACK_ENDPOINT (repo variable) and DOCS_FEEDBACK_TOKEN (repo secret) to the build step of build-docs.yml, for main only, and previews never get them (docs-preview.yml, and docusaurus.config.ts forces both to empty when preview_build is set). But build-docs.yml only builds and tests; nothing in this repo deploys the live site. So the pipeline that builds production must set these two variables too, or the fallback stays off. Who owns that pipeline? Until it's configured, merging this changes nothing for readers.
There was a problem hiding this comment.
Thanks, and sorry: my original comment pointed you at the wrong workflow. docs.scandit.com is built and published by generate-docusaurus-documentation-production in the data-capture-sdk GitLab project, on an hourly schedule. It clones main here and runs npm run build.
I opened a GitLab MR that passes the two variables to that job only: https://gitlab.scandit.com/mobile/data-capture-sdk/-/merge_requests/39858
- It maps
DOCS_FEEDBACK_ENDPOINT_PRODandDOCS_FEEDBACK_TOKEN_PRODto the namesdocusaurus.config.tsreads. The staging job runs the same script, so it doesn't get them and can't post into the production sheet. - A GitLab maintainer still has to create those two CI/CD variables. Ideally do that before this PR merges: the production job skips the build while the docs commit is unchanged, so variables added later only take effect with the next commit to
main. - The
env:block inbuild-docs.ymldoesn't affect production. Keeping it is fine, but the comment above it shouldn't suggest it's what turns the fallback on. Dropping it would also let you remove the GitHub repo variable and secret.
I'll leave this thread open until the GitLab side is configured.
There was a problem hiding this comment.
Reopening this one. @eugenia-scandit, I think cd2d304 should be reverted. It doesn't match how the merged GitLab MR (https://gitlab.scandit.com/mobile/data-capture-sdk/-/merge_requests/39858) works.
The MR doesn't give the docs build the _PROD names. It reads them and passes them on under the plain names, in the production job only:
# generate-docusaurus-documentation-production
DOCS_FEEDBACK_ENDPOINT: $DOCS_FEEDBACK_ENDPOINT_PROD
DOCS_FEEDBACK_TOKEN: $DOCS_FEEDBACK_TOKEN_PRODThe _PROD names exist so the docs build never reads them directly. The staging job (generate-docusaurus-documentation-staging) runs the same build script. If docusaurus.config.ts reads DOCS_FEEDBACK_ENDPOINT_PROD and the variables are created at project level, staging gets them too, and staging.docs.scandit.com posts into the production sheet. The MR's mapping would also have nothing left to read it.
With the config back on DOCS_FEEDBACK_ENDPOINT / DOCS_FEEDBACK_TOKEN (and build-docs.yml the same), it works as intended. Only production sees the values, and they can be masked project variables.
Still to do before this is done:
- Revert cd2d304.
- Create
DOCS_FEEDBACK_ENDPOINT_PROD(the Web App/execURL) andDOCS_FEEDBACK_TOKEN_PRODas CI/CD variables inmobile/data-capture-sdk. Neither exists yet. You have the values; @moritzhartmeier can add them. Ideally do this before this PR merges: the production job only rebuilds when the docs commit changes.
9c8795e to
20aa16d
Compare
The feedback widget sends votes and comments to PostHog, which only loads after a reader accepts cookies. A reader who declines and then writes a comment currently leaves no trace of it. When PostHog refuses the capture, the comment is now posted to a configurable endpoint instead (an Apps Script Web App that appends it to a spreadsheet). It is sent only when PostHog refused, never alongside a successful capture, so nothing is recorded twice. The payload is the four fields the widget already sends (URL, title, vote, comment), with no identifier. The vote stays three-valued: a missing vote is sent as null, not as "not helpful". The endpoint and token come from DOCS_FEEDBACK_ENDPOINT and DOCS_FEEDBACK_TOKEN at build time. Both default to empty, which disables the fallback and leaves the widget behaving exactly as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The analytics helper now reports why a capture did not happen: PostHog not loaded, the reader opted out, or PostHog threw. The boolean helper other components use keeps its exact behaviour. The feedback widget posts a comment to the fallback endpoint only when PostHog is not loaded; an opted-out reader's comment is never sent anywhere. The note under the comment box now says where a comment will go before the reader sends it. When the fallback applies, it names the Google Apps Script form and spreadsheet, the page address and title, and that the request carries the browser's IP address and user agent. Submitting is guarded against a second send while one is in flight, and Send is marked disabled meanwhile. After the request settles, the current outcome is read from a ref, and nothing updates the widget if it has unmounted. Failure and success copy follow the path taken: a PostHog refusal keeps the "analytics is turned off or blocked" text; a failed fallback request says the connection failed or was blocked. Because the fallback uses no-cors and cannot see the response, its thanks says only that the note was sent. The endpoint and token are passed to the build of the main branch only. Pull-request builds get empty values, and preview builds force the fallback off in the config. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Otherwise the vote could also reach PostHog if the reader accepted cookies a few seconds later, and be recorded twice. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The docs publishing script sets the production values as DOCS_FEEDBACK_ENDPOINT_PROD and DOCS_FEEDBACK_TOKEN_PROD, so read those names in docusaurus.config.ts and pass them under the same names in build-docs.yml. Preview builds still leave them unset. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This reverts commit cd2d3046cb4a86c4c1de7ec62e42f198ea79056f. The build reads the plain names. The production publishing job maps its production-only values onto them, so a staging build of the same script never sees them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… request - The config and the CI workflow named the CI build as the one that sets the endpoint; it checks the site and publishes nothing. The comments now say the publishing job sets it. - The token comment no longer claims the receiving end stops all abuse. - The held vote is cleared before the fallback request rather than after, so a PostHog flush while the request is in flight cannot record the same vote twice; it is restored if the request fails. - direct.ts: the body also carries the token. Small tidy-ups. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…while Clearing the held vote before the fallback request left two gaps when the request failed: the retry poll, which stops once it sees no held vote, was never restarted; and a vote the reader cast during the request (and PostHog then recorded) could be overwritten by the restored older one and sent a second time with the opposite value. A vote counter now decides whether to restore, and restoring restarts the poll. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A vote cast while the fallback request was in flight could disagree with the one the request carried and be recorded in two places. The thumbs are now aria-disabled and ignored during the send, like the Send button. A vote restored after the widget unmounted mid-request gets one capture attempt instead of being dropped. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
cd2d304 to
1a874cf
Compare
The capture helpers now sit on main's shared callPostHog (#484), and translate its outcome (sent / unavailable / opted-out / failed) into the four results the feedback fallback decides on. The fallback still fires only when PostHog is not loaded. The workflow keeps main's C API test step and this branch's build-step comment and values. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The page feedback widget ("Was this page helpful?") sends votes and comments to PostHog, which only loads after a reader accepts cookies. A reader who declines and then writes a comment currently leaves no trace of it.
What
null, never as "not helpful".no-corswith atext/plainbody, because an Apps Script Web App doesn't answer a CORS preflight. The response is opaque, so this reports that the request was sent, not that it arrived. The widget's copy already promises only "sent".DOCS_FEEDBACK_ENDPOINTandDOCS_FEEDBACK_TOKENat build time (customFieldsindocusaurus.config.ts). Both default to empty, which disables the fallback; with them unset, the widget behaves exactly as today. The token is not a secret: it ships in the client bundle and only filters untargeted requests, so the receiving script must validate and rate-limit.Before this does anything in production
DOCS_FEEDBACK_ENDPOINTandDOCS_FEEDBACK_TOKEN; no other build sets them, and preview builds force them off. The CI variablesDOCS_FEEDBACK_ENDPOINT_PRODandDOCS_FEEDBACK_TOKEN_PRODthat job reads must exist before this merges.Checks
npm run buildpasses;tscis clean.🤖 Generated with Claude Code